package dslProgramGen.methodCall;

import dslProgramGen.DslProgramGen;
import dslProgramGen.WhoPointToThisObjectCollection;
import utils.Utils;

import java.util.Map;

import static dslProgramGen.dslStatement.DslStatement.postProcessDslStatementAssignToObjFieldOrArrayIndex;
import static dslProgramGen.dslStatement.DslStatement.postProcessDslStatementNullAssignToObjFieldOrArrayIndex;
import static dslProgramGen.util.DslProgramGenUtil.*;

public class MethodReturnObj {

    /**
     * 将函数返回值 赋值给A.field trace转DSL 产生等号左边的DSL语句
     * 生成 A.field = f(); DSL语句
     * field可能为静态字段也可能为实例字段
     *
     * @param matchLine               与函数返回值匹配的Put trace
     * @param isStatic                字段是否为静态字段
     * @param objId2objExpressionsMap 指定对象id在DSL程序中可以怎样表示出来 <objId, expressions>
     * @param dslObjFieldToObjIdMap   给定DSL语句（对象.field 或 对象数组[index]），看看该DSL语句指向哪个对象Id，主要是帮助处理Put与PutA的trace，减少查询时间
     * @return 等号左边的DSL语句，例如 A.field
     */
    public static String genMethodReturnObjAssignToObjFieldEqualSignLeftDsl(String matchLine,
                                                                            boolean isStatic,
                                                                            Map<String, WhoPointToThisObjectCollection> objId2objExpressionsMap,
                                                                            Map<String, String> dslObjFieldToObjIdMap) {
        // 修改静态字段例子 Put H1:0x0000000046cdf8bd, C1:Student, IS:true, H2:0x000000000f0c8a99, C2:Course, F:112, T:main, M:java/lang/module/Configuration.<init> (Ljava/util/List;Ljava/lang/module/Resolver;)V
        // 修改实例字段例子 Put H1:0x000000003de64145, C1:java/lang/module/Configuration, IS:false, H2:0x000000001ecd979e, C2:java/util/ImmutableCollections$List12, F:12, T:main, M:java/lang/module/Configuration.<init> (Ljava/util/List;Ljava/lang/module/Resolver;)V
        String leftStatement = null;
        String h2ObjId;
        if (isStatic) {
            // 修改的是静态字段
            // 生成DSL语句，例如 Student.field = f();
            // 获得C1的类名称
            String c1ClassName = Utils.RegularExpressionProcess(matchLine, "C1:(.*?),");
            c1ClassName = renameClassNameReplaceSlashWithUnderlineAndRenameObjectClassName(c1ClassName);
            // 获得修改C1的字段名
            String fieldName = Utils.RegularExpressionProcess(matchLine, "F:(.*?),");
            // 获得H2的id
            h2ObjId = Utils.RegularExpressionProcess(matchLine, "H2:(.*?),");
            // 判断 H2是否为NULL
            // 修改静态字段例子 Put H1:0x0000000046cdf8bd, C1:Student, IS:true, H2:NULL, C2:NULL, F:112, T:main, M:java/lang/module/Configuration.<init> (Ljava/util/List;Ljava/lang/module/Resolver;)V
            if ("NULL".equals(h2ObjId)) {
                // 如果H2为null
                // 生成DSL语句，例如 Student.field = f();
                leftStatement = c1ClassName + "." + fieldName;

                postProcessDslStatementNullAssignToObjFieldOrArrayIndex(leftStatement,
                        objId2objExpressionsMap,
                        dslObjFieldToObjIdMap);
            } else {
                // H2不为null
                // 生成DSL语句，例如 Student.field = f();
                leftStatement = c1ClassName + "." + fieldName;

                // 更新两个数据结构 objId2objExpressionsMap 与 dslObjFieldToObjIdMap
                postProcessDslStatementAssignToObjFieldOrArrayIndex(leftStatement, h2ObjId,
                        objId2objExpressionsMap,
                        dslObjFieldToObjIdMap);
            }
        } else {
            // 修改的是实例字段
            // 生成DSL语句，例如 A.field = f();
            // 获得H1的id
            String h1ObjId = Utils.RegularExpressionProcess(matchLine, "H1:(.*?),");
            // 获得H2的id
            h2ObjId = Utils.RegularExpressionProcess(matchLine, "H2:(.*?),");
            // 获得修改H1的字段名
            String fieldName = Utils.RegularExpressionProcess(matchLine, "F:(.*?),");
            // 获得H1对象的dsl表示
            if (tryToGetDslExpressionOfObj(h1ObjId, objId2objExpressionsMap) == null) {
                return leftStatement;
            }
            String h1DslExpression = getDslExpressionOfObj(h1ObjId, objId2objExpressionsMap);
            // 看看H2是否为NULL
            if ("NULL".equals(h2ObjId)) {
                // 如果H2为null
                leftStatement = h1DslExpression + "." + fieldName;

                // 更新两个数据结构 objId2objExpressionsMap 与 dslObjFieldToObjIdMap
                postProcessDslStatementNullAssignToObjFieldOrArrayIndex(leftStatement,
                        objId2objExpressionsMap,
                        dslObjFieldToObjIdMap);
            } else {
                // H2不为null

                leftStatement = h1DslExpression + "." + fieldName;

                // 更新两个数据结构 objId2objExpressionsMap 与 dslObjFieldToObjIdMap
                postProcessDslStatementAssignToObjFieldOrArrayIndex(leftStatement, h2ObjId,
                        objId2objExpressionsMap,
                        dslObjFieldToObjIdMap);
            }

        }
        return leftStatement;
    }

    /**
     * 将函数返回值 赋值给array[index] trace转DSL 产生等号左边的DSL语句
     * 生成 array[index] = f(); DSL语句
     *
     * @param matchLine               与函数返回值匹配的PutA trace
     * @param objId2objExpressionsMap 指定对象id在DSL程序中可以怎样表示出来 <objId, expressions>
     * @param dslObjFieldToObjIdMap   给定DSL语句（对象.field 或 对象数组[index]），看看该DSL语句指向哪个对象Id，主要是帮助处理Put与PutA的trace，减少查询时间
     * @return 等号左边的DSL语句，例如 array[index]
     */
    public static String genMethodReturnObjAssignToArrayIndexEqualSignLeftDsl(String matchLine,
                                                                              Map<String, WhoPointToThisObjectCollection> objId2objExpressionsMap,
                                                                              Map<String, String> dslObjFieldToObjIdMap) {
        // 修改数组index例子 PutA H1:0x0000000046cdf8bd, C1:[[Ljava/lang/invoke/LambdaForm$Name;, H2:0x000000000f0c8a99, C2:[Ljava/lang/invoke/LambdaForm$Name;, O:0, T:main, M:java/lang/invoke/LambdaForm.<clinit> ()V
        // 生成DSL语句，例如 array[index] = dslStatement;
        String leftStatement;
        // 获得H1的id
        String h1ObjId = Utils.RegularExpressionProcess(matchLine, "H1:(.*?),");
        // 获得修改H1的index
        String index = Utils.RegularExpressionProcess(matchLine, "O:(.*?),");
        // H1可能在objId2objExpressionsMap中找不到
        if (DslProgramGen.ignoreNullPointerException) {
            if (tryToGetDslExpressionOfObj(h1ObjId, objId2objExpressionsMap) == null) {
                return null;
            }
        }
        // 获得H1对象的dsl表示
        String h1DslExpression = getDslExpressionOfObj(h1ObjId, objId2objExpressionsMap);
        // 获得H2的id
        String h2ObjId = Utils.RegularExpressionProcess(matchLine, "H2:(.*?),");
        // H2可能为NULL
        if ("NULL".equals(h2ObjId)) {
            // H2为NULL
            // 修改数组index例子 PutA H1:0x0000000046cdf8bd, C1:[[Ljava/lang/invoke/LambdaForm$Name;, H2:NULL, C2:NULL, O:0, T:main, M:java/lang/invoke/LambdaForm.<clinit> ()V
            // 生成DSL语句，例如 array[index] = f();
            leftStatement = h1DslExpression + "[" + index + "]";

            // 更新两个数据结构 objId2objExpressionsMap 与 dslObjFieldToObjIdMap
            postProcessDslStatementNullAssignToObjFieldOrArrayIndex(leftStatement,
                    objId2objExpressionsMap,
                    dslObjFieldToObjIdMap);
        } else {
            // H2不为NULL
            // 生成DSL语句，例如 array[index] = f();
            leftStatement = h1DslExpression + "[" + index + "]";

            // 更新两个数据结构 objId2objExpressionsMap 与 dslObjFieldToObjIdMap
            postProcessDslStatementAssignToObjFieldOrArrayIndex(leftStatement, h2ObjId,
                    objId2objExpressionsMap,
                    dslObjFieldToObjIdMap);
        }

        return leftStatement;
    }

    /**
     * 生成函数调用赋值给新建局部变量语句 Student stu = f();的左表达式 即Student stu
     * @param hId 新建局部变量id
     * @param hType 新建局部变量类型
     * @param objId2objExpressionsMap 指定对象id在DSL程序中可以怎样表示出来 <objId, expressions>
     * @param classTypeAndNumMap local root命名的辅助结构， 对于新建local root例如Student stu，如何命名stu，当前采用 "student"+num的方式表示
     * @param equalSingLeftObjType 等号左边对象类型
     * @return 等号左边的DSL语句，例如 Student stu
     */
    public static String genAddNewLocalRootDslStatementForMethodReturnEqualSignLeft(String hId, String hType,
                                                                                    Map<String, WhoPointToThisObjectCollection> objId2objExpressionsMap,
                                                                                    Map<String, Long> classTypeAndNumMap,
                                                                                    StringBuilder equalSingLeftObjType) {
        String leftStatement = null;
        if (!"NULL".equals(hId)) { // 如果hId不为null， hId可能为null，比如 Student stu = null;，不处理这种情况
            // 生成DSL语句，例如 Student stu = f(); 或 Student[][] stu = f();
            // 如果要创建local root，先获得local root的序号
            long prNum = getLocalRootNum(hType, classTypeAndNumMap);
            String prVariableName;
            // 判断local root是不是数组类型
            if (isArrayType(hType)) {
                // local root为数组类型
                // 获得数组elementType
                String elementType = getElementTypeFromArrayObjectType(hType);
                // 获得数组对象的维度
                int dimNum = getDimNumFromArrayObjectType(hType);
                // 组成local root的全名 小写(element类型)_array_维度_prNum
                prVariableName = elementType.toLowerCase() + "_array_" + dimNum + "_" + prNum;

                String squareBracket = "";
                for (int i = 0; i < dimNum; i++) {
                    squareBracket += "[]";
                }

                leftStatement = elementType + squareBracket + " " + prVariableName;
            } else {
                // local root为普通对象类型
                // 组成local root的全名 小写(类型)_prNum
                prVariableName = hType.toLowerCase() + "_" + prNum;

                leftStatement = hType + " " + prVariableName;
                equalSingLeftObjType.append(hType);
            }
            // 将新建的local root添加到objId2objExpressionsMap
            if (objId2objExpressionsMap.get(hId) == null) {
                objId2objExpressionsMap.put(hId, new WhoPointToThisObjectCollection(hType, hId));
            }
            objId2objExpressionsMap.get(hId).lrAndPrPointToThisObjectList.add(prVariableName);
        }
        return leftStatement;
    }

    /**
     * 将函数返回值 赋值给local root trace转DSL 产生等号左边的DSL语句
     * 生成 Student stu = f(); 或 stu = f(); DSL语句
     *
     * @param matchLine               与函数返回值匹配的Add LR trace
     * @param objId2objExpressionsMap 指定对象id在DSL程序中可以怎样表示出来 <objId, expressions>
     * @param classTypeAndNumMap      local root命名的辅助结构， 对于新建local root例如Student stu，如何命名stu，当前采用 "student"+num的方式表示
     * @return 等号左边的DSL语句，例如 stu
     */
    public static String genMethodReturnObjAssignToLocalRootEqualSignLeftDsl(String matchLine,
                                                                             Map<String, WhoPointToThisObjectCollection> objId2objExpressionsMap,
                                                                             Map<String, Long> classTypeAndNumMap, StringBuilder equalSingLeftObjType) {
        // 新建local root例子 Add LR H:0x000000004edde6e5, C:java/lang/ThreadGroup, T:main, R: NULL, M:java/lang/ThreadGroup.addUnstarted ()V
        // 给local root赋值例子 Add LR H:0x000000000d716361, C:java/lang/Class, T:main, R:0x000000004459eb14, M:java/util/ImmutableCollections$MapN.<init> ([Ljava/lang/Object;)V
        String leftStatement = null;
        // 首先获得R部分内容
        String contentOfR = Utils.RegularExpressionProcess(matchLine, "R: ?(.*?),");
        // 获得H的内容
        String hId = Utils.RegularExpressionProcess(matchLine, "H:(.*?),");
        // 获得类型
        String hType = Utils.RegularExpressionProcess(matchLine, "C:(.*?),");
        hType = renameClassNameReplaceSlashWithUnderlineAndRenameObjectClassName(hType);
        if (contentOfR.equals("NULL")) { // 表示新增一个gc root
            leftStatement = genAddNewLocalRootDslStatementForMethodReturnEqualSignLeft(hId, hType,
                    objId2objExpressionsMap,
                    classTypeAndNumMap,
                    equalSingLeftObjType);
        } else { // 表示修改一个gc root
            // hId可能为NULL
            if ("NULL".equals(hId)) {
                // hId为null
                // 生成DSL语句，例如 stu = f();
                // R的内容就是之前local root指向的对象id，获得local root的dsl表示
                if (DslProgramGen.ignoreNullPointerException) {
                    if (objId2objExpressionsMap.get(contentOfR) == null || objId2objExpressionsMap.get(contentOfR).lrAndPrPointToThisObjectList.size() == 0) {
                        return leftStatement;
                    }
                }

                leftStatement = objId2objExpressionsMap.get(contentOfR).lrAndPrPointToThisObjectList.get(0);

                // 从谁指向R的local root列表中移除leftStatement
                objId2objExpressionsMap.get(contentOfR).lrAndPrPointToThisObjectList.remove(0);
            } else {
                // hId不为null
                // 生成DSL语句，例如 stu = stu1;
                // R的内容就是之前local root指向的对象id，获得local root的dsl表示
                if (DslProgramGen.ignoreNullPointerException) {
                    if (objId2objExpressionsMap.get(contentOfR) == null || objId2objExpressionsMap.get(contentOfR).lrAndPrPointToThisObjectList.size() == 0) {
                        return leftStatement;
                    }
                }

                // 获得R对象类型
                String rType = objId2objExpressionsMap.get(contentOfR).objectType;
                if (rType.equals(hType)) {
                    // 如果H 和 R对象类型一致
                    leftStatement = objId2objExpressionsMap.get(contentOfR).lrAndPrPointToThisObjectList.get(0);

                    // 将local root新指向的对象添加到objId2objExpressionsMap
                    if (objId2objExpressionsMap.get(hId) == null) {
                        objId2objExpressionsMap.put(hId, new WhoPointToThisObjectCollection(hType, hId));
                    }
                    objId2objExpressionsMap.get(hId).lrAndPrPointToThisObjectList.add(leftStatement);
                    // 从谁指向R的local root列表中移除leftStatement
                    objId2objExpressionsMap.get(contentOfR).lrAndPrPointToThisObjectList.remove(0);
                } else {
                    // 如果H和R类型不一致，leftStatement为新定义的一个局部变量
                    leftStatement = genAddNewLocalRootDslStatementForMethodReturnEqualSignLeft(hId, hType,
                            objId2objExpressionsMap,
                            classTypeAndNumMap,
                            equalSingLeftObjType);
                    // 从谁指向R的local root列表中移除leftStatement
                    objId2objExpressionsMap.get(contentOfR).lrAndPrPointToThisObjectList.remove(0);
                }


            }
        }
        return leftStatement;
    }


    /**
     * Return 函数返回值 trace转DSL
     * 本来应该生成 Return f(); DSL语句，为简化该过程，分为两步
     * 1. 将f()赋值给临时gc root 例如 Student tempStu = f();
     * 2. 后续处理Return语句的时候 产生 Return tempStu,并且从谁指向新对象的 methodReturnForReturnTempLocalRootList列表中删除 tempStu
     * 该函数主要是产生临时gc root赋值语句，即第一步工作
     *
     * @param matchLine               与函数返回值匹配的Return trace
     * @param objId2objExpressionsMap 指定对象id在DSL程序中可以怎样表示出来 <objId, expressions>
     * @param classTypeAndNumMap      local root命名的辅助结构， 对于新建local root例如Student stu，如何命名stu，当前采用 "student"+num的方式表示
     * @return 等号左边的DSL语句，例如 Student stu
     */
    public static String genMethodReturnObjAssignToTempLocalRootForReturn(String matchLine,
                                                                          Map<String, WhoPointToThisObjectCollection> objId2objExpressionsMap,
                                                                          Map<String, Long> classTypeAndNumMap) {
        // Return H:0x0000000038e1d2c4, C:Student, T:main, M:Test.myFunctionNoArgAndReturn ()LStudent;
        // Return H:NULL, C:NULL, T:main, M:Test.main ([Ljava/lang/String;)V
        // 获得hId
        String hId = Utils.RegularExpressionProcess(matchLine, "H:(.*?),");
        // 获得htype
        String hType = Utils.RegularExpressionProcess(matchLine, "C:(.*?),");
        hType = renameClassNameReplaceSlashWithUnderlineAndRenameObjectClassName(hType);
        String leftStatement = null;
        // 生成DSL语句，例如 Student tempStu = f();
        if (!"NULL".equals(hId)) { // 如果hId不为null， hId可能为null，比如 return f();，f()返回值为null
            // 生成DSL语句，例如 Student stu = f(); 或 Student[][] stu = f();
            // 如果要创建local root，先获得local root的序号
            long prNum = getLocalRootNum(hType, classTypeAndNumMap);
            String prVariableName;
            // 判断local root是不是数组类型
            if (isArrayType(hType)) {
                // local root为数组类型
                // 获得数组elementType
                String elementType = getElementTypeFromArrayObjectType(hType);
                // 组成临时local root的全名 小写(类型)_array_tempLocalRootForReturn_prNum
                prVariableName = elementType.toLowerCase() + "_array_tempLocalRootForReturn_" + prNum;
                // 获得数组对象的维度
                int dimNum = getDimNumFromArrayObjectType(hType);
                String squareBracket = "";
                for (int i = 0; i < dimNum; i++) {
                    squareBracket += "[]";
                }

                leftStatement = elementType + squareBracket + " " + prVariableName;
            } else {
                // local root为普通对象类型
                // 组成临时local root的全名 小写(类型)_tempLocalRootForReturn_prNum
                prVariableName = hType.toLowerCase() + "_tempLocalRootForReturn_" + prNum;

                leftStatement = hType + " " + prVariableName;
            }
            // 将新建的local root添加到objId2objExpressionsMap
            if (objId2objExpressionsMap.get(hId) == null) {
                // 函数返回值返回的对象可能在caller中没有
                // 将函数返回值返回的对象添加到objId2objExpressionsMap，只是这里添加到的是methodReturnForReturnTempLocalRootList
                WhoPointToThisObjectCollection whoPointToThisObjectCollection = new WhoPointToThisObjectCollection(hType, hId);
                whoPointToThisObjectCollection.methodReturnForReturnTempLocalRootList.add(prVariableName);
                objId2objExpressionsMap.put(hId, whoPointToThisObjectCollection);
            } else {
                objId2objExpressionsMap.get(hId).methodReturnForReturnTempLocalRootList.add(prVariableName);
            }


        }
        return leftStatement;
    }


    /**
     * 函数返回值作为参数传给另一个函数 trace转DSL
     * 本来应该生成 f(g()); DSL语句，为简化该过程，分为两步
     * 1. 将g()赋值给临时gc root 例如 Student tempStu = g();
     * 该函数主要是产生临时gc root赋值语句，即第一步工作
     *
     * @param matchLine               函数的Return trace
     * @param objId2objExpressionsMap 指定对象id在DSL程序中可以怎样表示出来 <objId, expressions>
     * @param classTypeAndNumMap      local root命名的辅助结构， 对于新建local root例如Student stu，如何命名stu，当前采用 "student"+num的方式表示
     * @return 等号左边的DSL语句，例如 Student stu
     */
    public static String genMethodReturnObjAssignToTempLocalRootForMethodParameter(String matchLine,
                                                                                   Map<String, WhoPointToThisObjectCollection> objId2objExpressionsMap,
                                                                                   Map<String, Long> classTypeAndNumMap) {
        // Return H:0x0000000038e1d2c4, C:Student, T:main, M:Test.myFunctionNoArgAndReturn ()LStudent;
        // Return H:NULL, C:NULL, T:main, M:Test.main ([Ljava/lang/String;)V
        // 获得hId
        String hId = Utils.RegularExpressionProcess(matchLine, "H:(.*?),");
        // 获得htype
        String hType = Utils.RegularExpressionProcess(matchLine, "C:(.*?),");
        hType = renameClassNameReplaceSlashWithUnderlineAndRenameObjectClassName(hType);
        String leftStatement = null;
        // 生成DSL语句，例如 Student tempStu = f();
        if (!"NULL".equals(hId)) { // 如果hId不为null， hId可能为null，比如 return f();，f()返回值为null
            // 生成DSL语句，例如 Student stu = f(); 或 Student[][] stu = f();
            // 如果要创建local root，先获得local root的序号
            long prNum = getLocalRootNum(hType, classTypeAndNumMap);
            String prVariableName;
            // 判断local root是不是数组类型
            if (isArrayType(hType)) {
                // local root为数组类型
                // 获得数组elementType
                String elementType = getElementTypeFromArrayObjectType(hType);
                // 组成临时local root的全名 小写(类型)_array_tempLocalRootForReturn_prNum
                prVariableName = elementType.toLowerCase() + "_array_tempLocalRootForMethodReturnAssignToMethodParameter_" + prNum;
                // 获得数组对象的维度
                int dimNum = getDimNumFromArrayObjectType(hType);
                String squareBracket = "";
                for (int i = 0; i < dimNum; i++) {
                    squareBracket += "[]";
                }

                leftStatement = elementType + squareBracket + " " + prVariableName;
            } else {
                // local root为普通对象类型
                // 组成临时local root的全名 小写(类型)_tempLocalRootForReturn_prNum
                prVariableName = hType.toLowerCase() + "_tempLocalRootForMethodReturnAssignToMethodParameter_" + prNum;

                leftStatement = hType + " " + prVariableName;
            }
            // 将新建的local root添加到objId2objExpressionsMap
            if (objId2objExpressionsMap.get(hId) == null) {
                // 函数返回值返回的对象可能在caller中没有
                // 将函数返回值返回的对象添加到objId2objExpressionsMap，只是这里添加到的是methodReturnForReturnTempLocalRootList
                WhoPointToThisObjectCollection whoPointToThisObjectCollection = new WhoPointToThisObjectCollection(hType, hId);
                whoPointToThisObjectCollection.methodReturnForMethodParameterTempLocalRootList.add(prVariableName);
                objId2objExpressionsMap.put(hId, whoPointToThisObjectCollection);
            } else {
                objId2objExpressionsMap.get(hId).methodReturnForMethodParameterTempLocalRootList.add(prVariableName);
            }


        }
        return leftStatement;
    }
}
